﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Text;
using HtmlAgilityPack;
using Microsoft.Phone.Controls;
using nyxwin.Common.Silverlight.Commands;
using nyxwin.Common.Providers;

namespace nyxwin.Common.Silverlight.Controls
{
    public partial class HtmlContentTextBox : UserControl
    {
        private static Command OpenInBrowserCommand = new Command(new Action<object>((x) =>
        {
            string param = x as string;

            if (param != null)
                LinkHandler.Instance.OpenInBrowser(param);
        }));

        private static Command FollowLinkCommand = new Command(new Action<object>((x) =>
        {
            string param = x as string;

            if (param != null)
                LinkHandler.Instance.FollowLink(param);
        }));

        public HtmlContentTextBox()
        {
            InitializeComponent();
        }

        public string HtmlContent
        {
            get { return (string)GetValue(HtmlContentProperty); }
            set { SetValue(HtmlContentProperty, value); }
        }

        // Using a DependencyProperty as the backing store for HtmlContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HtmlContentProperty =
            DependencyProperty.Register("HtmlContent", typeof(string), typeof(HtmlContentTextBox), new PropertyMetadata(null,
                (obj, evArgs) =>
                {
                    var hctb = obj as HtmlContentTextBox;

                    if (hctb != null)
                    {
                        hctb.ProcessContent(evArgs.NewValue as string);
                    }
                }));

        private void ProcessContent(string html)
        {
            if (string.IsNullOrEmpty(html))
                html = "";

            try
            {
                HtmlDocument doc = new HtmlDocument();
                doc.LoadHtml(html);
                var ss = TransformParagraph(doc.DocumentNode);

                rtb.Blocks.Clear();

                foreach (var s in ss)
                {
                    rtb.Blocks.Add(s);
                }
            }
            catch (Exception e)
            {
                rtb.Blocks.Clear();

                Paragraph p = new Paragraph();
                p.Inlines.Clear();

                p.Inlines.Add(new LineBreak());
                p.Inlines.Add(new Run() { Text = "---UNHANDLED EXCEPTION---" });
                p.Inlines.Add(new LineBreak());
                p.Inlines.Add(new Run() { Text = e.Message });
                p.Inlines.Add(new LineBreak());
                p.Inlines.Add(new Run() { Text = "---HTML BODY---" });
                p.Inlines.Add(new LineBreak());
                p.Inlines.Add(new Run() { Text = html });
                p.Inlines.Add(new LineBreak());
                p.Inlines.Add(new Run { Text = "---UNHANDLED EXCEPTION END---" });
                p.Inlines.Add(new LineBreak());

                rtb.Blocks.Add(p);
            }
        }

        private IEnumerable<Block> TransformParagraph(HtmlNode htmlNode)
        {
            var paragraph = new Paragraph();

            foreach (var node in htmlNode.ChildNodes)
            {
                if (node.Name.ToLower() == "p")
                {
                    yield return paragraph;
                    foreach (var item in TransformParagraph(node))
                    {
                        yield return item;
                    }
                    paragraph = new Paragraph();
                }

                foreach (var item in TransformInline(node))
                {
                    paragraph.Inlines.Add(item);
                }
            }

            yield return paragraph;
        }

        private IEnumerable<Inline> TransformInline(HtmlNode node)
        {
            if (node is HtmlTextNode)
            {
                yield return new Run() { Text = node.InnerText.Trim() + " " };
            }
            else
            {
                if (node.Name.ToLower() == "br")
                {
                    yield return new LineBreak();
                }
                else if (node.Name.ToLower() == "b")
                {
                    var b = new Bold();

                    foreach (var item in node.ChildNodes)
                    {
                        foreach (var gen in TransformInline(item))
                        {
                            b.Inlines.Add(gen);
                        }
                    }

                    yield return b;
                }
                else if (node.Name.ToLower() == "i")
                {
                    var i = new Italic();

                    foreach (var item in node.ChildNodes)
                    {
                        foreach (var gen in TransformInline(item))
                        {
                            i.Inlines.Add(gen);
                        }
                    }

                    yield return i;
                }
                else if (node.Name.ToLower() == "a")
                {
                    var h = new Hyperlink();
                    h.Foreground = rtb.Resources["PhoneAccentBrush"] as Brush;
                    h.MouseOverForeground = h.Foreground;
                    string href = null;

                    var hrefAtt = node.ChildAttributes("href");
                    if (hrefAtt != null && hrefAtt.Count() > 0)
                    {
                        href = hrefAtt.First().Value;
                    }

                    if (!string.IsNullOrEmpty(href))
                    {
                        h.Command = FollowLinkCommand;
                        h.CommandParameter = href;
                    }

                    if ((node.ChildNodes.Count == 1) && (node.ChildNodes.First().Name == "img"))
                    {
                        yield return GenerateImage(node.ChildNodes.First(), node);
                    }
                    else
                    {
                        foreach (var item in node.ChildNodes)
                        {
                            foreach (var gen in TransformInline(item))
                            {
                                if (gen is LineBreak)
                                {
                                    var hh = new Hyperlink();
                                    hh.Foreground = h.Foreground;
                                    hh.MouseOverForeground = h.MouseOverForeground;
                                    hh.Command = FollowLinkCommand;
                                    hh.CommandParameter = href;

                                    yield return h;
                                    yield return new LineBreak();
                                    h = hh;
                                }
                                else if (gen is InlineUIContainer)
                                {
                                    if (h.Inlines.Count > 0)
                                    {
                                        var hh = new Hyperlink();
                                        hh.Foreground = h.Foreground;
                                        hh.MouseOverForeground = h.MouseOverForeground;
                                        hh.Command = FollowLinkCommand;
                                        hh.CommandParameter = href;
                                        yield return h;
                                        h = hh;
                                    }

                                    var content = ((InlineUIContainer)gen).Child;

                                    if (content != null)
                                    {
                                        var cm = content.GetValue(ContextMenuService.ContextMenuProperty) as ContextMenu;

                                        if (cm != null)
                                        {
                                            var mi = new MenuItem();
                                            mi.Header = "Follow link";
                                            mi.Command = FollowLinkCommand;
                                            mi.CommandParameter = href;
                                            cm.Items.Add(mi);
                                        }
                                    }

                                    yield return gen;
                                }
                                else
                                {
                                    h.Inlines.Add(gen);
                                }
                            }
                        }

                        yield return h;
                    }
                }
                else if (node.NodeType == HtmlNodeType.Comment)
                {
                    // Ignore comment
                }
                else if (node.Name.ToLower() == "img")
                {
                    yield return GenerateImage(node, null);
                }
                else
                {
                    yield return new LineBreak();
                    yield return new Run() { Text = "UNHANDLED CONTENT BEGIN" };
                    yield return new LineBreak();
                    yield return new Run() { Text = node.OuterHtml.Trim() };
                    yield return new LineBreak();
                    yield return new Run { Text = "UNHANDLED CONTENT END" };
                    yield return new LineBreak();
                }
            }
        }

        private Inline GenerateImage(HtmlNode imageNode, HtmlNode hrefNode)
        {
            InlineUIContainer cont = new InlineUIContainer();
            Image img = new Image();
            cont.Child = img;
            img.Stretch = Stretch.None;
            img.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(imageNode.Attributes["src"].Value, UriKind.RelativeOrAbsolute));

            ContextMenu cm = new ContextMenu();

            var mi = new MenuItem();
            mi.Header = "Open image";
            mi.Command = OpenInBrowserCommand;

            if (hrefNode != null)
            {
                mi.CommandParameter = hrefNode.Attributes["href"].Value;
            }
            else
            {
                mi.CommandParameter = imageNode.Attributes["src"].Value;
            }

            cm.Items.Add(mi);

            img.SetValue(ContextMenuService.ContextMenuProperty, cm);

            img.Tap += new EventHandler<System.Windows.Input.GestureEventArgs>(img_Tap);

            return cont;
        }

        void img_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            var s = sender as Image;

            if (s != null)
            {
                var cm = s.GetValue(ContextMenuService.ContextMenuProperty) as ContextMenu;
                
                if(cm != null)
                    cm.IsOpen = true;
            }
        }

        private void HyperlinkClicked(string href)
        {
            LinkHandler.Instance.FollowLink(href);
        }
    }
}
